#include "symbolTable.h"
#include <assert.h>

/* add symbol at the end of the list */
/* return pointer */
symbol*  addSymbol(symbol s)
{
 
  elem_symbol* newElem=malloc(sizeof(*newElem));
  assert(newElem);
  newElem->head=s;
  newElem->tail=NULL;
  if (TABLE->endListPtr)
    {
      /* there is at least one elem, at the end of the table */

      /* set the old last elem to the new last */
      TABLE->endListPtr->tail=newElem;
      /* set the endptr to the new last elem */
      TABLE->endListPtr=newElem;
    }
  else
    {
      /* first elem of this elem_table */
      TABLE->list=newElem;
      TABLE->endListPtr=newElem;
    }
  TABLE->size++;
  /* get the pointer */
  return &(TABLE->endListPtr->head);
}

void pushContext()
{
  /* we create a new context */
  elem_table* newTable=(elem_table*)malloc(sizeof(*newTable));
  assert(newTable);
  /* link to previous last table */
  newTable->next=TABLE;
  newTable->size=0;
  newTable->list=NULL;
  newTable->endListPtr=NULL;
  /* alter stack's head */
  TABLE=newTable;
}

void popContext()
{
  /* we free the list of symbols*/
  freeSymbolList(TABLE->list);

  /* save what we are going to free (current context) */
  elem_table* current=TABLE;
  /* take the next context as current */
  TABLE=TABLE->next;
  /* free the old current context */
  free(current);
}

void freeSymbolList(elem_symbol* list)
{
  elem_symbol* tmp=list;
  while(tmp)
    {
      tmp=tmp->tail;
      /* we use list as second pointer */
      free(list);
      list=tmp;
    }
}

void printTable()
{
  unsigned int i=0;
  unsigned int contextNumber=0;
  elem_table* context=TABLE;
  /* for all context */
  while(context)
    {
      elem_symbol* iterator=context->list;
      printf("***context number %d\n",contextNumber);
      /* print current context */
      for (i=0;i<context->size;i++)
	{
	  printf("%p\t",&(iterator->head));
	  printSymbol(iterator->head);
	  iterator=iterator->tail;
	}
      contextNumber++;
      context=context->next;
    }
}

/* return pointer on symbol or NULL */
symbol* searchSymbol(char* idName)
{
  symbol* ptr=NULL;
  elem_table* context=TABLE;
  while(context)
    {
      elem_symbol* iterator=context->list;
      while((iterator))
	{
	  if ((iterator->head.type==S_ID)
	      &&(!strcmp(iterator->head.val.name,idName)))
	    {
	      /* we've found it */
	      /* we stop iterator and set ptr */
	      ptr=&(iterator->head);
	      break;
	    }
	  iterator=iterator->tail;
	}
      context=context->next;
    }
  return ptr;
}

void initTable()
{
  /* the very first symbol table level */
  TABLE=(elem_table*)malloc(sizeof(*TABLE));
  TABLE->next=NULL; 
  TABLE->list=NULL; 
  TABLE->endListPtr=NULL; 
  TABLE->size=0;

  /* add size of word in the table */
  int wordSize=4;
  symbol news=newSymbol(S_CST,&wordSize,TYPE_INT);
  /* the table holds pointer to this symbol */
  TABLE->ptrToWordSize=addSymbol(news);

  /* add true cst to the symbol table */
  int trueVal=1;
  news=newSymbol(S_CST,&trueVal,TYPE_BOOL);
  /* the table holds pointer to this symbol */
  TABLE->ptrToTrueCst=addSymbol(news);

  /* add false cst to the symbol table */
  int falseVal=0;
  news=newSymbol(S_CST,&falseVal,TYPE_BOOL);
  /* the table holds pointer to this symbol */
  TABLE->ptrToFalseCst=addSymbol(news);
}

symbol* newTmp(enum val_t varType)
{
  static unsigned int tmpId=0;
  char tmpName[56];
  /* fill memory with blanks there */
  memset(tmpName,'\0',strlen(tmpName));
  sprintf(tmpName,"tmp%d",tmpId);

  char* tmpFinalName=strdup(tmpName);
  symbol news=newSymbol(S_TMP,tmpFinalName,varType);
  tmpId++;
  return addSymbol(news);
}


struct elem_symbol* cons_s(struct elem_symbol* list, symbol s)
{
  struct elem_symbol* tmp=list;
  struct elem_symbol* newElem=malloc(sizeof(*newElem));
  newElem->head=s;
  newElem->tail=NULL;
  if (tmp)
    {
      while(tmp->tail!=NULL)
	tmp=tmp->tail;
      tmp->tail=newElem;
    }
  else
    {
      list=newElem;
    }
  return list;
}

struct elem_symbol* nil_s()
{
  return NULL;
}

struct elem_symbol* concat_s(struct elem_symbol* list1,struct elem_symbol* list2)
{
  struct elem_symbol* tmp=list1;
  if (tmp)
    {
      while(tmp->tail)
	tmp=tmp->tail;
      tmp->tail=list2;
    }
  else
    {
      list1=list2;
    }
  return list1;
}


void addBunchOfSymbols(struct elem_symbol* list)
{
  struct elem_symbol* tmp=list;
  while(tmp)
    {
      addSymbol(tmp->head);
      tmp=tmp->tail;
    }
  freelist_s(list);
}

void freelist_s(struct elem_symbol* list)
{
  /* recursive function */
  if (!list)
    {
      return;
    }
  else
    {
      freelist_s(list->tail);
      free(list);
      return;
    }
}

symbol* getPtrToWordSize()
{
  return TABLE->ptrToWordSize;
}

symbol* getPtrToTrueCst()
{
  return TABLE->ptrToTrueCst;
}
symbol* getPtrToFalseCst()
{
  return TABLE->ptrToFalseCst;
}

